package group.sicher.lock.brain;

import group.sicher.lock.annotation.MarsKey;
import group.sicher.lock.annotation.MarsLock;
import group.sicher.lock.brain.lock.MarsKeyClassLock;
import group.sicher.lock.brain.lock.MarsKeyLockAbstractor;
import group.sicher.lock.brain.lock.MarsKeyMethodLock;
import group.sicher.lock.brain.lock.MarsKeyParameterLock;
import group.sicher.lock.component.MarsLockInterface;
import group.sicher.lock.factory.MarsComponentFactory;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @description: 锁注解拦截器
 * @author: aron
 * @date: 2019-08-05 11:56
 */
@Slf4j
@Aspect
@Component
public class MarsLockInterceptor {


    @Autowired
    private MarsComponentFactory marsComponentFactory;

    private ThreadLocal<MarsLockInterface> marsLockThreadLocal = new ThreadLocal<>();


    /**
     * aop切面 环绕
     * 拦截被@MarsLock注解的方法
     *
     * @param point
     * @return
     * @throws Throwable
     */
    @Around(value = "@annotation(group.sicher.lock.annotation.MarsLock)")
    public Object lockCenter(ProceedingJoinPoint point) throws Throwable {

        /**
         * 获取目标对象
         */
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method targetMethod = methodSignature.getMethod();
        String methodName = methodSignature.getName();
        Method realMethod = point.getTarget().getClass().getDeclaredMethod(methodName, targetMethod.getParameterTypes());

        /**
         * 获取注解 的参数
         */
        MarsLock lock = realMethod.getAnnotation(MarsLock.class);
        Object[] args = point.getArgs();
        String className = point.getTarget().getClass().getName();
        MarsKeyLockAbstractor keyLock = getKeyLock(className, methodName, realMethod, args);

        /**
         * 获取锁的属性 构建一个锁属性对象
         */
        MarsLockKey marsLockKey = new MarsKeyLockContext(keyLock).instance(lock);

        /**
         * 根据配置 获取基于Redisson的锁：可重入锁（默认） 公平锁 联锁 红锁 读锁 写锁
         */
        MarsLockInterface lockInterface = marsComponentFactory.getLock(lock.lockType());
        lockInterface.initMarsLockKey(marsLockKey);

        /**
         * 存储在当前线程的本地线程变量
         */
        marsLockThreadLocal.set(lockInterface);

        /**
         * 执行上锁的方法
         */
        lockInterface.lock();

        /**
         * 继续执行方法
         */
        return point.proceed();
    }

    /**
     * 方法执行后拦截：拦截被@MarsLock注解的方法
     * <p>
     * 释放锁
     *
     * @param point
     */
    @AfterReturning(value = "@annotation(group.sicher.lock.annotation.MarsLock)")
    public void after(JoinPoint point) {
        if (null != marsLockThreadLocal.get()) {
            marsLockThreadLocal.get().close();
        }
        /**
         * 记得删除本地线程的变量 避免污染全局
         */
        marsLockThreadLocal.remove();
    }

    /**
     * 方法执行异常：拦截被@MarsLock注解的方法
     *
     * @param point
     */
    @AfterThrowing(value = "@annotation(group.sicher.lock.annotation.MarsLock)")
    public void afterThrowable(JoinPoint point) {
        if (null != marsLockThreadLocal.get()) {
            marsLockThreadLocal.get().close();
        }
        /**
         * 记得删除本地线程的变量 避免污染全局
         */
        marsLockThreadLocal.remove();
    }


    /**
     * 获取被@marsKey 注解的参数或方法
     *
     * @param className
     * @param methodName
     * @param realMethod
     * @param args
     * @return
     */
    private MarsKeyLockAbstractor getKeyLock(String className, String methodName, Method realMethod, Object[] args) {
        /**
         *  参数锁
         */
        for (int i = 0; i < realMethod.getParameters().length; i++) {
            if (realMethod.getParameters()[i].isAnnotationPresent(MarsKey.class)) {
                return new MarsKeyParameterLock(className, methodName, realMethod, args);
            }
        }
        /**
         *  方法锁
         */
        if (null != realMethod.getAnnotation(MarsKey.class)) {
            return new MarsKeyMethodLock(className, methodName, realMethod, args);
        }

        /**
         * 未进行指定Key那么会默认锁整个方法
         */
        return new MarsKeyClassLock(className, methodName, realMethod, args);

    }

}
